#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/device.h>
#include "drv_pcie.h"

static u32 read_pcie_mem_le(void __iomem *mem)
{
    u32 val = 0;

    val = ioread32(mem);;
    pr_debug("Read:  mem=0x%px, val=0x%08x", mem, val);

    return val;
}

static void write_pcie_mem_le(void __iomem *mem, u32 val)
{
    iowrite32(val, mem);
    pr_debug("Write: mem=0x%px, val=0x%08x", mem, val);
}

int init_pcie_dev(struct pci_dev *pdev, struct pcie_dev_t *pcie_dev)
{
    int ret, i;
    resource_size_t bar_len;

    /* 检查参数是否有效 */
    if (!pdev || !pcie_dev) {
        pr_err("Invalid parameters for %s, %p, %p", 
               __func__, pdev, pcie_dev);
        return -EINVAL;
    }

    dev_dbg(&pdev->dev, "Initializing PCIe device %04x:%04x", 
            pdev->vendor, pdev->device);

    pcie_dev->pdev = pdev;
    spin_lock_init(&pcie_dev->splock);
    pcie_dev->read_mem = read_pcie_mem_le;
    pcie_dev->write_mem = write_pcie_mem_le;

    ret = pci_enable_device(pdev);
    if (ret) {
        dev_err(&pdev->dev, "Enable PCIe device failed, errno=%d", ret);
        return ret;
    }
    dev_dbg(&pdev->dev, "Enable PCIe device success");

    snprintf(pcie_dev->name, sizeof(pcie_dev->name), "%s_%04x:%02x:%02x.%d",
             PCI_DRV_NAME, pci_domain_nr(pdev->bus), pdev->bus->number,
             PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
    dev_dbg(&pdev->dev, "PCIe device name: %s", pcie_dev->name);

    ret = pci_request_regions(pdev, pcie_dev->name);
    if (ret) {
        dev_err(&pdev->dev, "Request PCIe regions failed, errno=%d", ret);
        goto err_disable_dev;
    }
    dev_dbg(&pdev->dev, "Request PCIe regions success");

    for (i = 0; i < PCI_STD_NUM_BARS; i++) {
        pcie_dev->bar[i] = NULL;
        bar_len = pci_resource_len(pdev, i);
        pcie_dev->bar_len[i] = bar_len;

        dev_dbg(&pdev->dev, "BAR%d: size %pa", i, &bar_len);

        /* 检查 BAR0 长度是否为 0 */
        if (i == 0 && bar_len == 0) {
            dev_err(&pdev->dev, "BAR0 has zero size");
            ret = -EIO;
            goto err_release_regions;
        }

        /* 只有在 BAR 长度大于 0 时才进行映射 */
        if (bar_len > 0) {
            pcie_dev->bar[i] = pci_ioremap_bar(pdev, i);
            if (!pcie_dev->bar[i]) {
                dev_err(&pdev->dev, "Map BAR%d failed", i);
                ret = -ENOMEM;
                goto err_unmap_bars;
            }

            dev_dbg(&pdev->dev, "BAR%d: %pR mapped at %px", 
                    i, &pdev->resource[i], pcie_dev->bar[i]);
        }
    }

    pci_set_master(pdev);
    dev_dbg(&pdev->dev, "Set PCIe bus master success");

    dev_dbg(&pdev->dev, "Init PCIe device success");

    return 0;

err_unmap_bars:
    for (; i >= 0; i--) {
        if (pcie_dev->bar[i]) {
            dev_dbg(&pdev->dev, "Unmapping BAR%d", i);
            iounmap(pcie_dev->bar[i]);
        }
    }
err_release_regions:
    pci_release_regions(pdev);
    dev_dbg(&pdev->dev, "PCIe regions released");
err_disable_dev:
    pci_disable_device(pdev);
    dev_dbg(&pdev->dev, "PCIe device disabled");

    return ret;
}

void exit_pcie_dev(struct pci_dev *pdev, struct pcie_dev_t *pcie_dev)
{
    int i;

    /* 检查参数是否有效 */
    if (!pdev || !pcie_dev) {
        pr_err("Invalid parameters for %s, %p, %p", 
               __func__, pdev, pcie_dev);
        return;
    }

    dev_dbg(&pdev->dev, "Uninitializing PCIe device");

    for (i = 0; i < PCI_STD_NUM_BARS; i++) {
        if (pcie_dev->bar[i]) {
            dev_dbg(&pdev->dev, "Unmapping BAR%d", i);
            pci_iounmap(pdev, pcie_dev->bar[i]);
            pcie_dev->bar[i] = NULL;
        }
    }

    pci_release_regions(pdev);
    dev_dbg(&pdev->dev, "PCIe regions released");

    pci_disable_device(pdev);
    dev_dbg(&pdev->dev, "PCIe device disabled");

    dev_dbg(&pdev->dev, "Exit PCIe device success");
}
